# -*- coding: utf-8 -*-
# =============================================================================
#     FileName:
#         Desc:
#       Author:
#        Email:
#     HomePage:
#      Version:
#   LastChange:
#      History:
# =============================================================================
import heapq
import logging

from rdbtools import RdbParser, MemoryCallback

from utils.common_config import CommonConfig

logger = logging.getLogger(name=CommonConfig.app_name)


class TopHeapItem(object):
    def __init__(self, item_key, item_data):
        self.item_key = item_key
        self.item_data = item_data

    def __cmp__(self, other):
        if self.item_key < other.item_key:
            return -1
        elif self.item_key > other.item_key:
            return 1
        else:
            return 0

    def __gt__(self, other):
        return self.item_key > other.item_key

    def __lt__(self, other):
        return self.item_key < other.item_key


class TopHeap(object):
    def __init__(self, top_size):
        self.top_size = top_size
        self.item_heap = []
        self.lwk_key = None

    def push_item(self, top_item):
        heapq.heappush(self.item_heap, top_item)
        if len(self.item_heap) > self.top_size:
            heapq.heappop(self.item_heap)
        if len(self.item_heap) == self.top_size:
            min_item = self.get_min_item()
            self.lwk_key = min_item.item_key

    def get_top_items(self):
        return [x for x in reversed([heapq.heappop(self.item_heap) for x in range(len(self.item_heap))])]

    def get_item_count(self):
        return len(self.item_heap)

    def get_min_item(self):
        items = self.get_top_items()
        if len(items) > 0:
            return items[-1]
        return None

    def get_max_item(self):
        items = self.get_top_items()
        if len(items) > 0:
            return items[0]
        return None

    def is_top_key(self, top_key):
        if len(self.item_heap) < self.top_size:
            return True
        if self.lwk_key < top_key:
            return True
        return False


class BigKeyCallback(object):
    def __init__(self, top_key_count=100, top_key_type="all", min_key_size=1024 * 1024):
        self.top_key_count = top_key_count
        self.top_key_type = top_key_type
        self.min_key_size = min_key_size
        self.top_key_heap = TopHeap(self.top_key_count)
        self.scan_key_index = 0
        self.init_env()

    def check_key_type(self, record):
        if self.top_key_type == "all":
            return True
        if str(self.top_key_type).lower() == str(record.type).lower():
            return True
        return False

    def next_record(self, record):
        self.scan_key_index += 1
        if self.scan_key_index % 1000 == 0:
            logger.info("扫描到第{}个Key".format(self.scan_key_index))
        if record.key is None:
            return
        if not self.check_key_type(record=record):
            return
        if record.bytes >= self.min_key_size and self.top_key_heap.is_top_key(record.bytes):
            top_item = TopHeapItem(item_key=record.bytes, item_data=record)
            self.top_key_heap.push_item(top_item=top_item)

    def end_rdb(self):
        logger.info("共扫描{}个key".format(self.scan_key_index))
        logger.info("完成加载数据...")

    def init_env(self):
        self.scan_key_index = 0
        logger.info("开始加载数据...")


class BigKeyParser(object):
    @classmethod
    def show_big_key(cls, redis_record):
        key_info = """
========================================
database：	{}
key:		{}
type:       {}
byte:		{}
encoding:	{}
size:		{}
expire:		{}
""".format(
            redis_record.database,
            redis_record.key,
            redis_record.type,
            redis_record.bytes,
            redis_record.encoding,
            redis_record.size,
            redis_record.expiry.isoformat() if redis_record.expiry else ''
        )
        logger.info(key_info)

    @classmethod
    def parse_rdb(cls, rdb_file_path, top_key_count=100,
                  top_key_type="all", min_key_size=1024 * 1024, ):
        redis_arch = 64
        string_escape = "raw"
        stream_handler = BigKeyCallback(
            top_key_count=top_key_count,
            top_key_type=top_key_type,
            min_key_size=min_key_size
        )
        parser = RdbParser(
            MemoryCallback(
                stream_handler,
                redis_arch,
                string_escape=string_escape
            )
        )
        parser.parse(rdb_file_path)
        top_items = stream_handler.top_key_heap.get_top_items()
        logger.info("排名TOP{}的key为：".format(top_key_count))
        for top_item in top_items:
            redis_record = top_item.item_data
            cls.show_big_key(redis_record)
        pass
